﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Zocono.Infrastructure.Common;
using Zocono.WCS.Domain.EntityInfo;
using Zocono.WCS.Infrastructure.PlcCommon;

namespace Zocono.WCS.Domain.PlcConnectPoolDomain
{
    public class PlcConnectionPool : IPlcConnectionPool
    {
        private readonly ILogUtil _logUtil;
        private List<PlcConnectionS7> s7PlcConnections;
        private List<long> s7DisConnectIDs;
        private List<string> s7LockPlcConnectionJobs;//锁定plc的JobName
        private Dictionary<long, int> s7HeartbeatException;
        private object s7LockObj;
        public PlcConnectionPool(ILogUtil logUtil)
        {
            _logUtil = logUtil;
            s7PlcConnections = new List<PlcConnectionS7>();
            s7DisConnectIDs = new List<long>();
            s7LockPlcConnectionJobs = new List<string>();
            s7HeartbeatException = new Dictionary<long, int>();
            s7LockObj = new object();
        }
        public bool S7LoadAllPlcConnection(List<PlcConnectionInfo> plcConnectionInfos)
        {

            foreach (var conn in plcConnectionInfos)
            {
                s7PlcConnections.Add(new PlcConnectionS7(conn.PlcCpuType, conn.IP, conn.Rack, conn.Slot, conn.ID, conn.IsSc, conn.ScEquipmentCode, _logUtil));
            }
            //_logUtil.Info($"s7PlcConnections: {JsonConvert.SerializeObject(s7PlcConnections)}");
            return true;
        }
        public List<long> S7GetReLoadPlcConnection()
        {
            return s7DisConnectIDs;
        }

        public bool S7ReLoadPlcConnection(long id, PlcConnectionInfo conn)
        {
            if (s7DisConnectIDs.Contains(id))
            {
                var res = Zocono.Infrastructure.Common.CommunicationHelper.IPService.Ping(conn.IP);
                if (res)
                {
                    s7PlcConnections.Add(new PlcConnectionS7(conn.PlcCpuType, conn.IP, conn.Rack, conn.Slot, conn.ID, conn.IsSc, conn.ScEquipmentCode, _logUtil));
                    s7DisConnectIDs.Remove(id);
                }
            }
            return true;
        }
        public PlcConnectionS7 S7GetReadPlcConnection(long plcConnectionID, string jobName)
        {
            //lock (s7LockObj)
            //{
            var s7PlcConnection = s7PlcConnections.Find(p => p.PlcConnectionID == plcConnectionID);

            if (s7PlcConnection == null)
            {
                _logUtil.Info($"获取PLC连接失败，PlcID：[{plcConnectionID}]不存在，jobName：[{jobName}]");
                return null;
            }
            return s7PlcConnection;

        }

        /// <summary>
        /// 添加被锁定设备
        /// </summary>
        /// <param name="jobName"></param>
        /// <returns></returns>
        public bool S7LockJobReadPlcConnection(string jobName)
        {
            lock (s7LockObj)
            {
                if (s7LockPlcConnectionJobs.Contains(jobName))
                {
                    return false;
                }
                s7LockPlcConnectionJobs.Add(jobName);
                return true;
            }
        }
        public bool S7FreeJobReadPlcConnection(string jobName)
        {
            lock (s7LockObj)
            {
                if (s7LockPlcConnectionJobs.Contains(jobName))
                {
                    s7LockPlcConnectionJobs.Remove(jobName);
                }
                return true;

            }
        }
        /// <summary>
        /// 锁定连接
        /// </summary>
        /// <param name="jobName"></param>
        /// <returns></returns>
        public bool S7CheckJobLockedConnect(string jobName)
        {
            if (s7LockPlcConnectionJobs.Count == 0)
            {
                return false;
            }
            return s7LockPlcConnectionJobs.Contains(jobName);

        }
        public PlcConnectionS7 S7GetWritePlcConnection(long plcConnectionID)
        {
            var s7PlcConnection = s7PlcConnections.Find(p => p.PlcConnectionID == plcConnectionID);

            if (s7PlcConnection == null)
            {
                //_logUtil.Info($"获取 WriteDB的 PLC连接失败，PlcID：[{plcConnectionID}]不存在 ");
                return null;
            }
            return s7PlcConnection;
        }

 



        public async Task<bool> S7Heartbeat(PlcReadDbInfo heartbeatDb)
        {
            var s7PlcConnection = s7PlcConnections.Find(p => p.PlcConnectionID == heartbeatDb.PlcConnectionID);
            try
            {
                if (s7PlcConnection == null)
                {
                    //_logUtil.Info($"PLC心跳：PLCID：[{heartbeatDb.PlcConnectionID}]不存在 ");
                    return false;
                }
                if (!s7PlcConnection.IsConnected)
                {
                    //bool isConnected = await s7PlcConnection.ConnectAsync();
                    //if (!isConnected) { return false; }s7PlcConnections
                    Thread.Sleep(500);
                    bool isConnected = await s7PlcConnection.ConnectAsync();
                    if (!isConnected)
                    {

                        s7DisConnectIDs.Add(s7PlcConnection.PlcConnectionID);
                        s7PlcConnection.DisConnect();
                        s7PlcConnections.Remove(s7PlcConnection);
                        _logUtil.Error($"S7HeartbeatRemove,{JsonConvert.SerializeObject(s7PlcConnection)}");
                        return false;
                    }
                }

                

            }
            catch (Exception ex)
            {
                _logUtil.Error($"心跳重连等报错，ex={ex.Message}");
            }

            return s7PlcConnection.IsConnected;
        }

        public bool S7CheckIdExists(long plcID)
        {
            return s7PlcConnections.Exists(p => p.PlcConnectionID == plcID);
        }

        /// <summary>
        /// 已知线程池有的，但没有实际业务，只拿来判断信号，需要另外使用。
        /// </summary>
        /// <param name="plcConnectionID">从PlcConnectionInfo表中获取线程池中有的IP地址</param>
        /// <param name="equipmentCode">从PlcConnectionInfo表中获取</param>
        /// <returns></returns>
        public PlcConnectionS7 S7GetReadSoloPlcConnection(long plcConnectionID, string equipmentCode)
        {
            var s7PlcConnection = s7PlcConnections.Find(p => p.PlcConnectionID == plcConnectionID);

            if (s7PlcConnection == null)
            {
                _logUtil.Info($"获取PLC连接失败，PlcID：[{plcConnectionID}]不存在，equipmentCode：[{equipmentCode}] ");
                return null;
            }
            return s7PlcConnection;
        }
    }
}
